/*
 *  (C) Copyright 2007 Insula Tecnologia da Informacao Ltda.
 * 
 *  This file is part of Jazz.
 *
 *  Jazz is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Jazz is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with Jazz.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.openinsula.jazz;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openinsula.jazz.aop.ValueBindingAdvice;
import org.openinsula.jazz.binding.BindingAttribute;
import org.openinsula.jazz.binding.BindingProvider;
import org.openinsula.jazz.resolver.DefaultResolverRegistry;
import org.openinsula.jazz.resolver.Resolver;
import org.openinsula.jazz.resolver.ResolverRegistry;
import org.openinsula.jazz.stereotype.Binding;
import org.springframework.beans.ConfigurablePropertyAccessor;
import org.springframework.beans.PropertyAccessorFactory;

/**
 * Default {@link BindingManager} implementation that uses Spring's
 * {@link ConfigurablePropertyAccessor} to copy <code>view to model</code> and
 * <code>model to view</code> properties.
 * 
 * @author yanaga
 * @since 1.0
 * @see BindingManager
 * @see Binding
 * @see ValueBindingAdvice
 */
public class DefaultBindingManager implements BindingManager {

	protected final Log logger = LogFactory.getLog(getClass());

	/**
	 * The {@link BindingProvider} used to lookup {@link BindingAttribute} for
	 * properties.
	 */
	private BindingProvider bindingProvider;

	/**
	 * The {@link ResolverRegistry} implementation that provides
	 * {@link Resolver} instances to get and set property values from view to
	 * model and vice-versa.
	 */
	private ResolverRegistry resolverRegistry = new DefaultResolverRegistry();

	@Override
	public void bindViewToModel() {
		List<BindingAttribute> bindingAttributes = bindingProvider.getBindingAttributes();
		for (BindingAttribute attribute : bindingAttributes) {
			Object field = attribute.getField();
			Object bean = attribute.getBean();
			String propertyName = attribute.getPropertyName();

			ConfigurablePropertyAccessor accessor = PropertyAccessorFactory.forBeanPropertyAccess(bean);

			Resolver resolver = resolverRegistry.getResolverForType(field.getClass());

			if (resolver != null) {
				Object value = resolver.getValue(field);

				if (logger.isDebugEnabled()) {
					logger.debug("Binding view field " + field.getClass() + " to model bean " + bean.getClass()
							+ ", property name " + propertyName + " with value " + value);
				}

				accessor.setPropertyValue(propertyName, value);
			}
			else {
				logger.warn("Unable to find Resolver for field of type " + field.getClass());
			}
		}
	}

	@Override
	public void bindModelToView() {
		List<BindingAttribute> bindingAttributes = bindingProvider.getBindingAttributes();
		for (BindingAttribute attribute : bindingAttributes) {
			Object field = attribute.getField();
			Object bean = attribute.getBean();
			String propertyName = attribute.getPropertyName();

			ConfigurablePropertyAccessor accessor = PropertyAccessorFactory.forBeanPropertyAccess(bean);

			Resolver resolver = resolverRegistry.getResolverForType(field.getClass());

			if (resolver != null) {
				Object propertyValue = accessor.getPropertyValue(propertyName);

				if (logger.isDebugEnabled()) {
					logger.debug("Binding model bean " + bean.getClass() + ", property name " + propertyName
							+ " to view field " + field.getClass() + " with value " + propertyValue);
				}

				resolver.setValue(field, propertyValue);
			}
			else {
				logger.warn("Unable to find Resolver for field of type " + field.getClass());
			}
		}
	}

	public void setBindingProvider(BindingProvider bindingProvider) {
		this.bindingProvider = bindingProvider;
	}

	public void setResolverRegistry(ResolverRegistry resolverRegistry) {
		this.resolverRegistry = resolverRegistry;
	}

}
